﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using Granite.Collections;
using Granite.Xaml;
using Rifts.Models.Characters;
using Rifts.Models.Definitions;

namespace Rifts.Creator
{

	public sealed class CreatorViewModel : NavigationViewModel
	{
		public CreatorViewModel(PrimaryViewModel primaryViewModel)
		{
			m_PrimaryViewModel = primaryViewModel;
			NavigationFormatString = @"\Creator\{0}.xaml";
			StepList.CollectionChanged += StepList_CollectionChanged;
		}
		
		public readonly Random Random = new Random();

        readonly PrimaryViewModel m_PrimaryViewModel;
		readonly Catalog m_Catalog = new Catalog();

		public IList<Race> Races
		{
			get { return m_Catalog.Races; }
		}

		internal void FirstPage()
		{
			Navigate("SelectRace");
		}

		public Character Character
		{
			get { return Get<Character>("Character"); }
			set
			{
				if (Set(value, "Character"))
				{
					PrintCommand.OnCanExecuteChanged();
				}
			}
		}

		public Race SelectedRace
		{
			get { return Get<Race>("SelectedRace"); }
			set { Set(value, "SelectedRace"); }
		}

		public CharacterClass SelectedClass
		{
			get { return Get<CharacterClass>("SelectedClass"); }
			set { Set(value, "SelectedClass"); }
		}

		public bool CanMoveNext
		{
			get { return Get<bool>(false, "CanMoveNext"); }
			set
			{
				if (Set(value, "CanMoveNext"))
					NavigateNextCommand.OnCanExecuteChanged();
			}
		}

		/// <summary>
		/// This locks in the changes for the current page and moves the character to the next step in the process.
		/// </summary>
		public DelegateCommand NavigateNextCommand
		{
			get
			{
				return GetCommand(() =>
				{
					Character.AcceptChanges();
					var nextStep = StepList[0];
					StepList.RemoveAt(0);
					ResetAction = null;
					Navigate(nextStep.Name);
					CanMoveNext = false;
				}, () => CanMoveNext && StepList.Count > 0, "NavigateNextCommand");
			}
		}

		public ObservableCollection<BuildStep> StepList
		{
			get { return GetNew<ObservableCollection<BuildStep>>("StepList"); }
		}

		void StepList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			NavigateNextCommand.OnCanExecuteChanged();
		}


		public ExtendedObservableCollection<CharacterClass> Classes
		{
			get { return GetNew<ExtendedObservableCollection<CharacterClass>>("Classes"); }
		}

		internal void UpdateClassList()
		{
			Classes.Clear();
			Classes.AddRange(SelectedRace.AllowedClasses(Character));
		}

		internal void ApplyRace(Race race)
		{
			Character = new Character();
			race.Apply(Random, Character);
			SelectedRace = race;

			CanMoveNext = true;
			StepList.Clear();
			race.BuildSteps(StepList);
		}

		internal void ApplyClass(CharacterClass occ)
		{
			occ.Apply(Random, Character);
			SelectedClass = occ;

			StepList.Clear();
			occ.BuildSteps(StepList);
			CanMoveNext = true;
		}

		public ExtendedObservableCollection<SkillDefinitionViewModel> OccSkills
		{
			get { return GetNew<ExtendedObservableCollection<SkillDefinitionViewModel>>("OccSkills"); }
		}

		public int OccSkillPicks
		{
			get { return Get<int>("OccSkillPicks"); }
			set { Set(value, "OccSkillPicks"); }
		}

		public ExtendedObservableCollection<ExtendedObservableCollection<SkillDefinitionViewModel>> RelatedSkills
		{
			get { return GetNew<ExtendedObservableCollection<ExtendedObservableCollection<SkillDefinitionViewModel>>>("RelatedSkills"); }
		}

		public int RelatedSkillPicks
		{
			get { return Get<int>("RelatedSkillPicks"); }
			set { Set(value, "RelatedSkillPicks"); }
		}

		public ExtendedObservableCollection<ExtendedObservableCollection<SkillDefinitionViewModel>> SecondarySkills
		{
			get { return GetNew<ExtendedObservableCollection<ExtendedObservableCollection<SkillDefinitionViewModel>>>("SecondarySkills"); }
		}

		public int SecondarySkillPicks
		{
			get { return Get<int>("SecondarySkillPicks"); }
			set { Set(value, "SecondarySkillPicks"); }
		}

		public void ApplyOccSkill(SkillDefinitionViewModel skillDefinition)
		{
			OccSkills.Remove(skillDefinition);
			OccSkillPicks -= 1;
			skillDefinition.Apply(Random, Character);
			UpdateOccSkills();
		}

		public void ApplyRelatedSkill(SkillDefinitionViewModel skillDefinition)
		{
			foreach (var group in RelatedSkills)
			{
				foreach (var skill in group)
				{
					if (skill == skillDefinition)
					{
						group.Remove(skillDefinition);
						goto RecordFound;
					}
				}
			}

		RecordFound:

			RelatedSkillPicks -= skillDefinition.Cost;
			skillDefinition.Apply(Random, Character);

			UpdateRelatedSkills();

		}

		public void ApplySecondarySkill(SkillDefinitionViewModel skillDefinition)
		{
			foreach (var group in SecondarySkills)
			{
				foreach (var skill in group)
				{
					if (skill == skillDefinition)
					{
						group.Remove(skillDefinition);
						goto RecordFound;
					}
				}
			}

		RecordFound:

			SecondarySkillPicks -= skillDefinition.Cost;
			skillDefinition.Apply(Random, Character);

			UpdateSecondarySkills();
		}

		public void UpdateOccSkills()
		{
			foreach (var skill in OccSkills)
				skill.UpdateFromCharacter(Character, true, OccSkillPicks);
		}

		public void UpdateRelatedSkills()
		{
			foreach (var group in RelatedSkills)
				foreach (var skill in group)
					skill.UpdateFromCharacter(Character, false, RelatedSkillPicks);
		}

		public void UpdateSecondarySkills()
		{
			foreach (var group in SecondarySkills)
				foreach (var skill in group)
					skill.UpdateFromCharacter(Character, false, SecondarySkillPicks);
		}

		public Action ResetAction
		{
			get { return Get<Action>("ResetAction"); }
			set
			{
				if (Set(value, "ResetAction"))
					ResetCommand.OnCanExecuteChanged();
			}
		}

		/// <summary>
		/// This locks in the changes for the current page and moves the character to the next step in the process.
		/// </summary>
		public DelegateCommand ResetCommand
		{
			get
			{
				return GetCommand(() => ResetAction(), () => ResetAction != null, "ResetCommand");
			}
		}

		public DelegateCommand PrintCommand
		{
			get { return GetCommand(() => m_PrimaryViewModel.Print(Character), () => Character != null, "PrintCommand"); }
		}

		public DelegateCommand SaveCommand
		{
			get { return GetCommand(() => m_PrimaryViewModel.Save(Character), () => false, "SaveCommand"); }
		}

	}
}




